﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Zellularautomat.Structures;
using Model;
using Interpreter;

namespace Zellularautomat
{
	public partial class FormPreferences : Form
	{
		//TODO müssen wir noch synchrone, sequentielle und zufällige Abarbeitung bereitstellen?

		private Controller controller;
		List<CellDefinition> cellDefinitions = new List<CellDefinition>();
		Dictionary<string, Color> colorsForCells = new Dictionary<string, Color>();
		Preferences preferences = new Preferences();
		int pxBetweenLines = 29;

		public Preferences Preferences
		{
			get { return preferences; }
			set { preferences = value; }
		}

		public List<CellDefinition> CellDefinitions
		{
			get { return cellDefinitions; }
			set { cellDefinitions = value; }
		}

		public Dictionary<string, Color> ColorsForCells
		{
			get { return colorsForCells; }
			set { colorsForCells = value; }
		}

		public FormPreferences(Controller controller)
		{
			this.controller = controller;
			InitializeComponent();
			cmBoxEnvironmentType.DataSource = Enum.GetValues(typeof(EnvironmentType));
		}

		#region CellTypes
		#region Events

		private void btnOpenCellDefinitionFile_Click(object sender, System.EventArgs e)
		{
			OpenFile(openFileDialogCells, txtBxCellDefinitionFileName);
		}

		private void btnLoadCellDefinitionFile_Click(object sender, System.EventArgs e)
		{
			if (File.Exists(txtBxCellDefinitionFileName.Text))
			{
				cellDefinitions = controller.LoadCellDefinitions(txtBxCellDefinitionFileName.Text);
				ShowCellTypes();
			}
			else
			{
				MessageBox.Show("Die ausgewählte Datei existiert nicht", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		void colorChangeButton_Click(object sender, System.EventArgs e)
		{
			if (colorDialog.ShowDialog() == DialogResult.OK)
			{
				colorsForCells[cellDefinitions[(int)((Button)sender).Tag].ZellTyp] = colorDialog.Color;
			}
		}

		private void groupBoxColorPicking_Paint(object sender, PaintEventArgs e)
		{
			for (int i = 0; i < cellDefinitions.Count; i++)
			{
				DrawRectangle(colorsForCells[cellDefinitions[i].ZellTyp], groupBoxColorPicking,
					new Point(100, 19 + i * pxBetweenLines), new Size(85, 23));
			}
		}

		#endregion
		#region Work

		private void ShowCellTypes()
		{
			List<HSBColor> colors = CreateColors(cellDefinitions.Count);
			for (int i = 0; i < cellDefinitions.Count; i++)
			{
				Label l = new Label()
				{
					Text = cellDefinitions[i].ZellTyp,
					Location = new Point(6, 24 + i * pxBetweenLines),
					Width = 94
				};
				Button b = new Button()
				{
					Text = "Farbe ändern",
					Size = new Size(85, 23),
					Location = new Point(200, 19 + i * pxBetweenLines),
					Tag = i,
					UseVisualStyleBackColor = true,
					TabStop = true,
					TabIndex = i
				};
				b.Click += colorChangeButton_Click;
				groupBoxColorPicking.Controls.Add(l);
				groupBoxColorPicking.Controls.Add(b);
				colorsForCells.Add(cellDefinitions[i].ZellTyp, colors[i].ToRGB());
			}
			groupBoxColorPicking.Paint += groupBoxColorPicking_Paint;
		}

		private List<HSBColor> CreateColors(int countOfDifferentColors)
		{
			List<HSBColor> colors = new List<HSBColor>();
			int distance = 360 / countOfDifferentColors;

			for (int i = 0; i < countOfDifferentColors; i++)
			{
				colors.Add(new HSBColor(i * distance, 1, 1));
			}

			return colors;
		}

		private void DrawRectangle(Color color, Control control, Point location, Size size)
		{
			SolidBrush myBrush = new SolidBrush(color);
			Graphics formGraphics;
			formGraphics = control.CreateGraphics();
			formGraphics.FillRectangle(myBrush, new Rectangle(location, size));
			myBrush.Dispose();
			formGraphics.Dispose();
		}

		#endregion
		#endregion

		#region Environment

		private void btnOpenRuleFile_Click(object sender, EventArgs e)
		{
			OpenFile(openFileDialogRule, txtBxRuleFileName);
		}

		private void btnLoadRuleFile_Click(object sender, EventArgs e)
		{
			if (File.Exists(txtBxRuleFileName.Text))
			{
				preferences = controller.LoadPreferences(txtBxRuleFileName.Text);
				ShowRules();
			}
			else
			{
				MessageBox.Show("Die ausgewählte Datei existiert nicht", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void ShowRules()
		{
			txtBxSizeHeight.Text = preferences.SizeHeight.ToString();
			txtBxSizeWidth.Text = preferences.SizeWidth.ToString();
			cmBoxEnvironmentType.SelectedItem = preferences.EnvironmentType;
			txtBxActualisationTime.Text = preferences.TimeTillActualisation.ToString();
		}

		#endregion

		#region Initialization
		#region Events

		private void tbCtrlPreferences_Selecting(object sender, TabControlCancelEventArgs e)
		{
			if (e.TabPage.Equals(tbPgInitialization) && cellDefinitions.Count == 0)
			{
				MessageBox.Show("Bitte laden Sie erst die verwendeten Zelltypen.", "Keine Daten vorhanden",
					MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				e.Cancel = true;
			}
		}

		private void tbPgInitialization_Enter(object sender, EventArgs e)
		{
			if (numericUpDowns.Count == 0)
			{
				ShowCellDistribution();
			}
		}

		private void numericUpDown_Click(object sender, EventArgs e)
		{
			lblSumOfCells.Text = GetSumOfCells().ToString();
		}

		#endregion
		#region Work
		private Dictionary<string, NumericUpDown> numericUpDowns = new Dictionary<string, NumericUpDown>();
		private Label lblSumOfCells;

		private void ShowCellDistribution()
		{
			Dictionary<string, decimal> cellDistribution = GetCellDistribution();

			for (int i = 0; i < cellDefinitions.Count; i++)
			{
				Label l = new Label()
				{
					Text = cellDefinitions[i].ZellTyp,
					Location = new Point(6, 8 + i * pxBetweenLines),
					Width = 80
				};
				NumericUpDown numericUpDown = new NumericUpDown()
				{
					DecimalPlaces = 2,
					Location = new Point(115, 6 + i * pxBetweenLines),
					Size = new System.Drawing.Size(55, 20),
					Tag = i,
					TabIndex = 2 * i,
					Value = cellDistribution[cellDefinitions[i].ZellTyp]
				};
				numericUpDowns.Add(cellDefinitions[i].ZellTyp, numericUpDown);
				numericUpDown.Click += numericUpDown_Click;
				tbPgInitialization.Controls.Add(l);
				tbPgInitialization.Controls.Add(numericUpDown);
			}
			lblSumOfCells = new Label()
			{
				Text = GetSumOfCells().ToString(),
				Location = new Point(115, 6 + cellDefinitions.Count * pxBetweenLines),
				Width = 80
			};
			tbPgInitialization.Controls.Add(lblSumOfCells);
		}

		private Dictionary<string, decimal> GetCellDistribution()
		{
			Dictionary<string, decimal> distribution = new Dictionary<string, decimal>();
			List<string> remainingCellTypes = new List<string>();

			foreach (var cellType in cellDefinitions)
			{
				if (preferences.CellDistribution.ContainsKey(cellType.ZellTyp))
				{
					distribution.Add(cellType.ZellTyp, preferences.CellDistribution[cellType.ZellTyp]);
				}
				else
				{
					remainingCellTypes.Add(cellType.ZellTyp);
				}
			}
			//Fill remaining cellTypes to 100%
			foreach (var remainingType in remainingCellTypes)
			{
				decimal value = 100m / remainingCellTypes.Count;
				distribution.Add(remainingType, value);
			}

			// Show Error message
			if (remainingCellTypes.Count > 0 && remainingCellTypes.Count < cellDefinitions.Count)
			{
				MessageBox.Show("Nicht für alle Zelltypen konnte die Anfangsverteilung geladen werden.",
					"Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}
			return distribution;
		}

		private decimal GetSumOfCells()
		{
			decimal count = 0;
			foreach (var numUpDown in numericUpDowns.Values)
			{
				count += numUpDown.Value;
			}
			return count;
		}

		#endregion
		#endregion

		#region Events

		private void btnOK_Click(object sender, System.EventArgs e)
		{
			DialogResult = DialogResult.OK;
		}

		private void FormPreferences_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (DialogResult == DialogResult.OK)
			{
				if (lblSumOfCells == null)
				{
					tbCtrlPreferences.SelectedTab = tbPgInitialization;
					Application.DoEvents();
					if (MessageBox.Show("Ist diese Anfangsverteilung richtig?", "Bitte bestätigen", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.No)
					{
						e.Cancel = true;
					}
				}
				try
				{
					ValidateUserInput();
				}
				catch (ArgumentException ae)
				{
					e.Cancel = true;
					DialogResult = System.Windows.Forms.DialogResult.Cancel;
					MessageBox.Show(ae.Message, "Eingabefehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
					return;
				}
				GetUIValues();
			}
		}

		#endregion

		#region Work

		private void OpenFile(OpenFileDialog openFileDialog, TextBox textBox)
		{
			if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				textBox.Text = openFileDialog.FileName;
			}
		}

		private void ValidateUserInput()
		{
			if (cellDefinitions == null || cellDefinitions.Count == 0)
			{
				throw new ArgumentException("Bitte geben Sie Zelltypen an.");
			}

			int sizeHeight;
			int sizeWidth;
			long timeTillAct;
			if (!int.TryParse(txtBxSizeHeight.Text, out sizeHeight))
			{
				throw new ArgumentException("Bitte geben Sie eine Ganzzahl für die Höhe an.");
			}
			if (!int.TryParse(txtBxSizeWidth.Text, out sizeWidth))
			{
				throw new ArgumentException("Bitte geben Sie eine Ganzzahl für die Breite an.");
			}
			if (!long.TryParse(txtBxActualisationTime.Text, out timeTillAct))
			{
				throw new ArgumentException("Bitte geben Sie eine Ganzzahl für die Aktualisierungszeit an.");
			}

			int maxWidth = GetMaxSizeWidth();
			int maxHeight = GetMaxSizeHeight();
			if (sizeWidth <= 0 || sizeWidth > maxWidth)
			{
				throw new ArgumentException("Breite muss zwischen 0 und " + maxWidth + " liegen.");
			}
			if (sizeHeight <= 0 || sizeHeight > maxHeight)
			{
				throw new ArgumentException("Höhe muss zwischen 0 und " + maxHeight + " liegen.");
			}

			decimal sumOfCells = decimal.Parse(lblSumOfCells.Text);
			if (sumOfCells != 100)
			{
				throw new ArgumentException("Die Summe der Anfangsverteilung muss 100% ergeben.");
			}
		}

		private int GetMaxSizeWidth()
		{
			return this.Owner.Size.Width - 16; // Borders right and left
		}

		private int GetMaxSizeHeight()
		{
			return this.Owner.Size.Height - 65; //Borders top and bottom, menu strip
		}

		private void GetUIValues()
		{
			preferences.EnvironmentType = (EnvironmentType)cmBoxEnvironmentType.SelectedItem;
			preferences.SizeHeight = int.Parse(txtBxSizeHeight.Text);
			preferences.SizeWidth = int.Parse(txtBxSizeWidth.Text);
			preferences.TimeTillActualisation = long.Parse(txtBxActualisationTime.Text);
			Dictionary<string, decimal> cellDistribution = new Dictionary<string, decimal>();
			foreach (var numUpDown in numericUpDowns)
			{
				cellDistribution.Add(numUpDown.Key, numUpDown.Value.Value);
			}
			preferences.CellDistribution = cellDistribution;
		}

		#endregion

	}
}
